﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Bricks.Data;
using NHibernate;

namespace Bricks.NHibernate.Data
{
    
    /// <summary>
    /// 
    /// </summary>
    public class QueryOverFacade : SessionFacadeBase, IQueryOverFacade
    {

        /// <summary>
        /// 
        /// </summary>
        /// <param name="session"></param>
        public QueryOverFacade(ISession session) :
            base(session)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="session"></param>
        /// <returns></returns>
        protected virtual IQueryOver<TEntity, TEntity> QueryOver<TEntity>(ISession session) where TEntity : class
        {
            return session.QueryOver<TEntity>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public virtual int Count<TEntity>(Func<IQueryOver<TEntity, TEntity>, IQueryOver<TEntity>> predicate) where TEntity : class
        {
            return Execute<int>(session => predicate.Invoke(QueryOver<TEntity>(session)).ToRowCountQuery().FutureValue<int>().Value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public override int Count<TEntity>(Expression<Func<TEntity, bool>> predicate)
        {
            return Count<TEntity>(query => query.Where(predicate));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public override int CountAll<TEntity>()
        {
            return Count<TEntity>(query => query);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public virtual IEnumerable<T> Query<TEntity, T>(Func<IQueryOver<TEntity, TEntity>, IQueryOver<TEntity>> predicate) where TEntity : class
        {
            return Execute<IEnumerable<T>>(session => predicate.Invoke(QueryOver<TEntity>(session)).Future<T>());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public override IEnumerable<TEntity> Query<TEntity>(Expression<Func<TEntity, bool>> predicate)
        {
            return Query<TEntity, TEntity>(query => query.Where(predicate));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public virtual PagedResult<T> Query<TEntity, T>(Func<IQueryOver<TEntity, TEntity>, IQueryOver<TEntity>> predicate, int pageIndex, int pageSize) where TEntity : class
        {
            return Execute<PagedResult<T>>(session =>
            {
                IQueryOver<TEntity> query = predicate.Invoke(QueryOver<TEntity>(session)).Skip((pageIndex - 1) * pageSize).Take(pageSize);

                return new PagedResult<T>() { PageOfResults = query.Future<T>(), TotalRecords = query.ToRowCountQuery().FutureValue<int>().Value };
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public override PagedResult<TEntity> Query<TEntity>(Expression<Func<TEntity, bool>> predicate, int pageIndex, int pageSize)
        {
            return Query<TEntity, TEntity>(query => query.Where(predicate), pageIndex, pageSize);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public override IEnumerable<TEntity> QueryAll<TEntity>()
        {
            return Query<TEntity, TEntity>(query => query);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public override PagedResult<TEntity> QueryAll<TEntity>(int pageIndex, int pageSize)
        {
            return Query<TEntity, TEntity>(query => query, pageIndex, pageSize);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public virtual T QuerySingle<TEntity, T>(Func<IQueryOver<TEntity, TEntity>, IQueryOver<TEntity>> predicate) where TEntity : class
        {
            return Execute<T>(session => predicate.Invoke(QueryOver<TEntity>(session)).FutureValue<T>().Value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public override TEntity QuerySingle<TEntity>(Expression<Func<TEntity, bool>> predicate)
        {
            return QuerySingle<TEntity, TEntity>(query => query.Where(predicate));
        }
    }
}
